package org.lwxyz.utils;

/**
 * @description 16进制字符串与字符串相互转化工具类
 * @author yuwei
 * @version 1.0-SNAPSHOT
 * @date 2018/1/23
 */
public final class HexUtil {

    /**
     * 用于建立十六进制字符的输出的小写字符数组
     */
    private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    /**
     * 用于建立十六进制字符的输出的大写字符数组
     */
    private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};


    /**
     * 将字节数组转换为十六进制字符串（大写）
     *
     * @param bytes 字节数组
     * @return 16进制字符串
     */
    public static String bytesToHexString(byte[] bytes) {
        return bytesToHexString(bytes, false);
    }

    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param bytes       字节数组
     * @param toLowerCase true传换成小写格式，false传换成大写格式
     * @return 十六进制字符串
     */
    public static String bytesToHexString(byte[] bytes, boolean toLowerCase) {
        return bytesToHexString(bytes, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
    }

    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param bytes    字节数组
     * @param toDigits 目标字符数组（DIGITS_LOWER或DIGITS_UPPER）
     * @return 十六进制字符串
     */
    private static String bytesToHexString(byte[] bytes, char[] toDigits) {
        return new String(bytesToHexChars(bytes, toDigits));
    }

    /**
     * 将字节数组转换为十六进制字符数组（大写）
     *
     * @param bytes 字节数组
     * @return 十六进制字符数组
     */
    public static char[] bytesToHexChars(byte[] bytes) {
        return bytesToHexChars(bytes, false);
    }

    /**
     * 将字节数组转换为十六进制字符数组
     *
     * @param bytes       字节数组
     * @param toLowerCase true传换成小写格式，false传换成大写格式
     * @return 十六进制字符数组
     */
    public static char[] bytesToHexChars(byte[] bytes, boolean toLowerCase) {
        return bytesToHexChars(bytes, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
    }

    /**
     * 将字节数组转换为十六进制字符数组
     *
     * @param bytes    字节数组
     * @param toDigits 目标字符数组（DIGITS_LOWER或DIGITS_UPPER）
     * @return 十六进制字符数组
     */
    private static char[] bytesToHexChars(byte[] bytes, char[] toDigits) {
        int l = bytes.length;
        char[] chars = new char[l << 1];
        for (int i = 0, j = 0; i < l; i++) {
            chars[j++] = toDigits[(0xF0 & bytes[i]) >>> 4];
            chars[j++] = toDigits[0x0F & bytes[i]];
        }
        return chars;
    }

    /**
     * 将十六进制字符串转换为字节数组
     *
     * @param string 16进制字符串
     * @return 字节数组
     */
    public static byte[] hexStringToBytes(String string) {
        return hexCharsToBytes(string.toCharArray());
    }

    /**
     * 将十六进制字符数组转换为字节数组
     *
     * @param chars 16进制字符数组
     * @return 字节数组
     */
    public static byte[] hexCharsToBytes(char[] chars) {
        int len = chars.length;
        if ((len & 0x01) != 0) {
            throw new RuntimeException("未知的字符");
        }
        byte[] out = new byte[len >> 1];
        for (int i = 0, j = 0; j < len; i++) {
            int f = toDigit(chars[j], j) << 4;
            j++;
            f = f | toDigit(chars[j], j);
            j++;
            out[i] = (byte) (f & 0xFF);
        }
        return out;
    }

    /**
     * 将十六进制字符转换成一个整数
     *
     * @param ch    字符
     * @param index 位置
     * @return ascii码
     */
    private static int toDigit(char ch, int index) {
        int digit = Character.digit(ch, 16);
        if (digit == -1) {
            throw new RuntimeException("非法16进制字符 " + ch + " 在索引 " + index);
        }
        return digit;
    }

}
